#include "Alumno.h"
#include "VentanaAlumno.h"
#include "ReceptorFramesAV.h"
//#include "ReceptorChat.h"

Alumno::Alumno(char* ip_serv, char* puerto, char* puerto_av) :
	Usuario(ip_serv, puerto, puerto_av) {
	this->receptor = NULL;
	this->reproductor_video = new ReproductorVideo();
	this->altavoz = new Altavoz();
	this->en_clase = false;
}

void Alumno::crear_hilos_de_recepcion() {
	this->receptor = new ReceptorFramesAV(socket_av, reproductor_video, altavoz, this);
	receptor->start();
}

// codOp indica si se quiere inscribir a una clase o un curso
int Alumno::Inscribir(char codOp, std::string codigo, std::string clave) {
	int ret = -1;
    unsigned int cod = 0;
    cod = this->String_toInt(codigo);
    std::string aux;

    // Codigo vacio
    if(codigo.size() <= 0) return -2;

    // Codigo invalido
    if(cod <= 0) return -3;

    aux = this->Int_toString(cod);

    // Ingrese numeros
    if(aux.size() != codigo.size()) return -4;

	switch(codOp) {
	case('A'): {
		ret = 0;
		if(buscarClase(String_toInt(codigo)))
			return -5;
		if((ret = Inscripcion('A', codigo, clave)) == 0)
			clases_inscriptos.push_back(String_toInt(codigo));
		break;
	}
	case('B'): {
		ret = 0;
		if(buscarCurso(String_toInt(codigo)))
			return -5;
		if((ret = Inscripcion('B', codigo, clave)) == 0)
			cursos_inscriptos.push_back(String_toInt(codigo));
		break;
	}
	}
	return ret;
}

// Inscribe a una clase o un curso
int Alumno::Inscripcion(char cod_op, std::string codigo, std::string clave) {
	char codRespuesta;
	int tam_arg = codigo.size() + 1;
	if (!socket->enviar(&cod_op, sizeof(cod_op))) {
		std::cerr << "No se pudo enviar" << std::endl;
		return -1;
	}
	if (!socket->enviar(&tam_arg, sizeof(tam_arg))) {
		std::cerr << "No se pudo enviar" << std::endl;
		return -1;
	}
	if (!socket->enviar(codigo.c_str(), codigo.size() + 1)) {
		std::cerr << "No se pudo enviar" << std::endl;
		return -1;
	}
	if(this->recibirRespuesta(&codRespuesta) == -1) {
		return -1;
	}
	if(codRespuesta == '2')
		return -5;
	if(codRespuesta == '0')
		return -6;
	if(codRespuesta == '1')
		return 0;
	return 1;
}

int Alumno::recibirCartelera() {
	char cod_op = 'C';
	int tam_arg = 0;
	char buffer[TAM_BLOQUE_INI];
	int tam_bloque_ini = sizeof(int) + sizeof(char);
	char *argumentos = NULL;
	std::string codigo, descripcion, codCurso;

	// Envio codigo para q me manda la cartelera
	if(!socket->enviar(&cod_op, sizeof(cod_op))) {
		std::cerr << "No se pudo enviar" << std::endl;
		return -1;
	}
	if(!socket->enviar(&tam_arg, sizeof(tam_arg))) {
		std::cerr << "No se pudo enviar" << std::endl;
		return -1;
	}

	while(1) {
		argumentos = NULL;
		if(!this->socket->recibir(buffer, tam_bloque_ini)) {
			std::cerr << "No se pudo recibir el codigo y el tamanio" << std::endl;
			break;
		}
		cod_op = buffer[0];
		if((cod_op != 'C') && (cod_op != 'D') && (cod_op != 'E')) {
			break;
		}
		tam_arg = *((unsigned int*)&buffer[1]);
		memmove(buffer, &buffer[4], TAM_BLOQUE_INI - 4);
		if(tam_arg > 0) {
			argumentos = new char[tam_arg];
			if(!this->socket->recibir(argumentos, tam_arg)) {
				std::cout << "No se pudo recibir " << std::endl;
				delete[] argumentos;
				break;
			}
			codigo = argumentos;
			tam_arg -= codigo.size();
			descripcion = "";
			if(tam_arg > 1) {
				descripcion = &argumentos[codigo.size() + 1];
			}
			if(cod_op == 'C')
				ventana->agregarClase(codigo, descripcion);
			if(cod_op == 'D')
				ventana->agregarCurso(codigo, descripcion);
			if(cod_op == 'E') {
				codCurso = &argumentos[codigo.size() + descripcion.size() + 2];
				ventana->agregarClaseDeCurso(codCurso, codigo, descripcion);
			}
			delete argumentos;
		}
	}
	return 0;
}

int Alumno::recibirCarteleraVideos() {
	char cod_op = 'D';
	int tam_arg = 0;
	char buffer[TAM_BLOQUE_INI];
	char *argumentos = NULL;
	std::string codClase, codCurso;

	// Envio codigo para q me manda la cartelera
	if(!socket->enviar(&cod_op, sizeof(cod_op))) {
		std::cerr << "No se pudo enviar" << std::endl;
		return -1;
	}
	if(!socket->enviar(&tam_arg, sizeof(tam_arg))) {
		std::cerr << "No se pudo enviar" << std::endl;
		return -1;
	}

	while(1) {
		argumentos = NULL;
		if(!socket->recibir(buffer, TAM_BLOQUE_INI)) {
			std::cerr << "No se pudo recibir el codigo y el tamanio" << std::endl;
			break;
		}
		cod_op = buffer[0];
		if((cod_op != 'A') && (cod_op != 'B')) {
			break;
		}
		tam_arg = *((unsigned int*)&buffer[1]);
		memmove(buffer, &buffer[4], TAM_BLOQUE_INI - 4);
		if(tam_arg > 0) {
			argumentos = new char[tam_arg];
			if(!this->socket->recibir(argumentos, tam_arg)) {
				std::cerr << "No se pudo recibir " << std::endl;
				delete[] argumentos;
				break;
			}
			codClase = argumentos;
			tam_arg -= codClase.size();
			codCurso = "0";
			if(tam_arg > 1) {
				codCurso = &argumentos[codClase.size() + 1];
			}
			ventana->agregarVideosDeClasesDispo(codClase, codCurso);
			delete argumentos;
		}
	}
	return 0;
}

int Alumno::bajarVideo(std::string codigo, std::string nombre_archivo) {
	char cod_op = 'E';
	int tam_arg = codigo.size() + 1;
	int tam_video = 0;
	char buffer[TAM_BLOQUE_INI];
	char *argumentos = NULL;
	std::ofstream video(nombre_archivo.c_str());

	// Envio el codigo para decir q video quiero
	if(this->enviarDatos(cod_op, tam_arg, codigo) == -1)
		return -1;

	argumentos = NULL;
	if(!socket->recibir(buffer, TAM_BLOQUE_INI)) {
		std::cerr << "No se pudo recibir el codigo y el tamanio" << std::endl;
		return -1;
	}
	cod_op = buffer[0];
	if(cod_op != 'A') {
		std::cerr << "Codigo invalido" << std::endl;
		return -1;
	}
	tam_video = *((unsigned int*)&buffer[1]);
	memmove(buffer, &buffer[4], TAM_BLOQUE_INI - 4);
	argumentos = new char[tam_video];
	if(tam_video > 0) {
		if(!socket->recibir(argumentos, tam_video)) {
			std::cerr << "No se pudo recibir " << std::endl;
			delete[] argumentos;
			return -1;
		}
		video.write(argumentos, tam_video);
		delete[] argumentos;
	}
	return 0;
}

int Alumno::recibirCarteleraAdjuntos() {
	char cod_op = 'I';
	int tam_arg = 0;
	char buffer[TAM_BLOQUE_INI];
	char *argumentos = NULL;
	std::string codClase, codCurso, nombreAdj;

	// Envio codigo para q me manda la cartelera
	if(!socket->enviar(&cod_op, sizeof(cod_op))) {
		std::cerr << "No se pudo enviar" << std::endl;
		return -1;
	}
	if(!socket->enviar(&tam_arg, sizeof(tam_arg))) {
		std::cerr << "No se pudo enviar" << std::endl;
		return -1;
	}

	while(1) {
		argumentos = NULL;
		if(!socket->recibir(buffer, TAM_BLOQUE_INI)) {
			std::cerr << "No se pudo recibir el codigo y el tamanio" << std::endl;
			break;
		}
		cod_op = buffer[0];
		if((cod_op != 'A') && (cod_op != 'B')) {
			break;
		}
		tam_arg = *((unsigned int*)&buffer[1]);
		memmove(buffer, &buffer[4], TAM_BLOQUE_INI - 4);
		if(tam_arg > 0) {
			argumentos = new char[tam_arg];
			if(!this->socket->recibir(argumentos, tam_arg)) {
				std::cerr << "No se pudo recibir " << std::endl;
				delete[] argumentos;
				break;
			}
			codClase = argumentos;
			tam_arg -= codClase.size() + 1;
			nombreAdj = &argumentos[codClase.size() + 1];
			tam_arg -= nombreAdj.size() + 1;
			if(tam_arg > 0) {
				codCurso = &argumentos[codClase.size() + 1 + nombreAdj.size() + 1];
			}
			ventana->agregarAdjuntosDeClasesDispo(codClase, codCurso, nombreAdj);
			delete argumentos;
		}
	}
	return 0;
}

int Alumno::bajarAdjunto(std::string nombre_archivo) {
	char cod_op = 'J';
	int tam_arg = nombre_archivo.size() + 1;
	int tam_adjunto = 0;
	char buffer[TAM_BLOQUE_INI];
	char *argumentos = NULL;
	std::ofstream adjunto(nombre_archivo.c_str());

	// Envio el codigo para decir q video quiero
	if(this->enviarDatos(cod_op, tam_arg, nombre_archivo) == -1)
		return -1;
	argumentos = NULL;
	if(!socket->recibir(buffer, TAM_BLOQUE_INI)) {
		std::cerr << "No se pudo recibir el codigo y el tamanio" << std::endl;
		return -1;
	}
	cod_op = buffer[0];
	if(cod_op != 'A') {
		std::cerr << "Codigo invalido" << std::endl;
		return -1;
	}
	tam_adjunto = *((unsigned int*)&buffer[1]);
	argumentos = new char[tam_adjunto];
	if(tam_adjunto > 0) {
		if(!socket->recibir(argumentos, tam_adjunto)) {
			std::cerr << "No se pudo recibir " << std::endl;
			delete[] argumentos;
			return -1;
		}
		adjunto.write(argumentos, tam_adjunto);
		delete[] argumentos;
	}
	return 0;
}

// Devuelte true si el usuario ya esta inscripto en esa clase
bool Alumno::buscarClase(unsigned int cod_Clase) {
	if(clases_inscriptos.size() <= 0)
		return false;
	std::list<unsigned int>::iterator it;
	for(it = clases_inscriptos.begin(); it != clases_inscriptos.end(); it++) {
		if((*it) == cod_Clase) {
			std::cerr << "Clase ya existente/inscripta" << std::endl;
			return true;
		}
	}
	std::cerr << "Clase inexistente/no inscripta" << std::endl;
	return false;
}

// Devuelte true si el usuario ya esta inscripto en ese curso
bool Alumno::buscarCurso(unsigned int cod_Clase) {
	if(cursos_inscriptos.size() <= 0)
		return false;
	std::list<unsigned int>::iterator it;
	for(it = cursos_inscriptos.begin(); it != cursos_inscriptos.end(); it++) {
		if((*it) == cod_Clase) {
			std::cerr << "Curso ya existente/inscripta" << std::endl;
			return true;
		}
	}
	std::cerr << "Curso inexistente/no inscripta" << std::endl;
	return false;
}

ReproductorVideo* Alumno::get_reproductor_video() {
	return this->reproductor_video;
}

bool Alumno::esta_en_clase() {
	return en_clase;
}
int Alumno::entro_a_clase(char* cod_clase, int tamanio_cod_clase) {
  this->en_clase = true;
  // Envia al servidor el codigo de la clase a la que entro
  char cod_op = 'G';
  if(!socket->enviar(&cod_op, sizeof(cod_op))) {
    std::cerr << "No se pudo enviar" << std::endl;
    return -1;
  }
  if(!socket->enviar(&tamanio_cod_clase, sizeof(tamanio_cod_clase))) {
    std::cerr << "No se pudo enviar" << std::endl;
    return -1;
  }
  if(!socket->enviar(cod_clase, tamanio_cod_clase)) {
      std::cerr << "No se pudo enviar" << std::endl;
      return -1;
  }
  //this->receptor_chat = new ReceptorChat(this, this->socket);
  //this->receptor_chat->start();
  return 0;
}
int Alumno::salio_de_clase(char* cod_clase, int tamanio_cod_clase) {
  this->en_clase = false;
  // Envia al servidor el codigo de la clase
  char cod_op = 'H';
  if(!socket->enviar(&cod_op, sizeof(cod_op))) {
    std::cerr << "No se pudo enviar" << std::endl;
    return -1;
  }
  if(!socket->enviar(&tamanio_cod_clase, sizeof(tamanio_cod_clase))) {
    std::cerr << "No se pudo enviar" << std::endl;
    return -1;
  }
  if(!socket->enviar(cod_clase, tamanio_cod_clase)) {
      std::cerr << "No se pudo enviar" << std::endl;
      return -1;
  }
//  this->receptor_chat->terminar_recepcion();
//  this->receptor_chat->join();
//  delete this->receptor_chat;
//  this->receptor_chat = NULL;
  return 0;
}

void Alumno::transmitiendo(bool trans) {
	ventana->anularBotones1(!trans);
	ventana->anularBotones2(!trans);
}

void Alumno::setVentana(VentanaAlumno *ven) {
	this->ventana = ven;
}

void Alumno::levantarVentana() {
	gdk_threads_enter();
	ventana->levantar();
	gdk_threads_leave();
}

Alumno::~Alumno() {
	if (conectado) {
		this->socket->shutDown();
		this->socket_av->shutDown();
		conectado = false;
	}
	if (receptor != NULL) {
		receptor->join();
		delete receptor;
	}
	delete this->reproductor_video;
	delete this->altavoz;
}
